#include "svdpi.h"
#include "common.h"

#define BITMASK(bits) ((1ull << (bits)) - 1)
#define BITS(x, hi, lo) (((x) >> (lo)) & BITMASK((hi) - (lo) + 1)) // similar to x[hi:lo] in verilog
#define SEXT(x, len) ({ struct { int64_t n : len; } __x = { .n = x }; (uint64_t)__x.n; })

#ifdef __cplusplus
extern "C"
{
#endif
    long long mul_caculate(svBit is_mulh, svBit src_word, long long src1, long long src2, svBit src1_sign, svBit src2_sign)
    {
        // Prepare src
        uint64_t usrc1 = src1;
        uint64_t usrc2 = src2;
        if (src_word)
        {
            usrc1 = (uint32_t)usrc1;
            usrc2 = (uint32_t)usrc2;
            if (src1_sign)
            {
                if ((int32_t)usrc1 < 0)
                {
                    usrc1 = BITS(-((int32_t)usrc1), 31, 0);
                }
            }
            if (src2_sign)
            {
                if ((int32_t)usrc2 < 0)
                {
                    usrc2 = BITS(-((int32_t)usrc2), 31, 0);
                }
            }
        } else {
            if (src1_sign)
            {
                if ((int64_t)usrc1 < 0)
                {
                    usrc1 = -usrc1;
                }
            }
            if (src2_sign)
            {
                if ((int64_t)usrc2 < 0)
                {
                    usrc2 = -usrc2;
                }
            }
        }
        // Calculate res sign
        bool res_sign = false;
        {
            bool s1 = false, s2 = false;
            if (src1_sign)
            {
                s1 = BITS(src1, 63, 63);
                if (src_word)
                {
                    s1 = BITS(src1, 31, 31);
                }
            }
            if (src2_sign)
            {
                s2 = BITS(src2, 63, 63);
                if (src_word)
                {
                    s2 = BITS(src2, 31, 31);
                }
            }
            res_sign = s1 ^ s2;
        }

        // Calculate Result
        uint64_t res = 0;
        __uint128_t x, y, res_128;
        x = usrc1;
        y = usrc2;
        res_128 = x * y;

        if (is_mulh)
        {
            res = (uint64_t)(res_128 >> 64);
        }
        else
        {
            res = res_128;
        }
        // Generate Result
        if (res_sign)
        {
            res = -res;
        }
        if (src_word)
        {
            res = SEXT(res, 32);
        }
        return res;
    }
    long long div_caculate(svBit src_word, long long src1, long long src2, svBit src_sign, svBit is_rem)
    {
        // Prepare src
        uint64_t usrc1 = src1;
        uint64_t usrc2 = src2;
        if (src_word)
        {
            usrc1 = BITS(usrc1, 31, 0);
            usrc2 = BITS(usrc2, 31, 0);
            if (src_sign)
            {
                if ((int32_t)usrc1 < 0)
                {
                    usrc1 = BITS(-((int32_t)usrc1), 31, 0);
                }
                if ((int32_t)usrc2 < 0)
                {
                    usrc2 = BITS(-((int32_t)usrc2), 31, 0);
                }
            }
        }
        else
        {
            if (src_sign)
            {
                if ((int64_t)usrc1 < 0)
                {
                    usrc1 = -((int64_t)usrc1);
                }
                if ((int64_t)usrc2 < 0)
                {
                    usrc2 = -((int64_t)usrc2);
                }
            }
        }
        // Calculate res sign
        bool res_sign = false;
        if (src_sign)
        {
            bool src1_sign = BITS(src1, 63, 63);
            bool src2_sign = BITS(src2, 63, 63);
            if (src_word)
            {
                src1_sign = BITS(src1, 31, 31);
                src2_sign = BITS(src2, 31, 31);
            }
            res_sign = src1_sign ^ src2_sign;
        }
        // Calculate Result
        uint64_t res = 0;
        if (is_rem)
        {
            res = usrc1;
            if (usrc2)
            {
                res = usrc1 % usrc2;
            }
        }
        else
        {
            res = -1ull;
            if (usrc2)
            {
                res = usrc1 / usrc2;
            }
        }
        // Generate Result
        if (res_sign)
        {
            res = -res;
        }
        if (src_word)
        {
            res = SEXT(res, 32);
        }
        return res;
    }

#ifdef __cplusplus
}
#endif
